#include <dspSimulator/instruction/ldw.h>

bool LDW::checkCondition(std::shared_ptr<DspMachine> dspMachine) {
    if (isCompactInstruction) {
        return true;
    } else {
        return checkStandardCondition(dspMachine);
    }
}

void LDW::loadInstIRIntoJit(llvm::LLVMContext *llvmContext, llvm::Module *module,
                            std::shared_ptr<llvm::IRBuilder<>> irBuilder) {

}

std::shared_ptr<Instruction> LDW::decode(std::shared_ptr<InstInfo> instInfo, u64 baseCycle) {
    std::shared_ptr<LDW> ldw;
    auto dspInstInfo = std::dynamic_pointer_cast<DspInstInfo>(instInfo);
    bool isStandardInst = dspInstInfo->isStandardInstruction();
    if (isStandardInst) {
        auto data = vectorToBitSet<32>(dspInstInfo->getBits());
        // LDW (.unit) *+baseR[offsetR], dst
        if (checkBits<5>(data, 2, 0b11001) && checkBits<1>(data, 8, 0b0) && existStandardCondition(data)) {
            ldw = std::make_shared<LDW>(dspInstInfo->getAddress(), baseCycle);
            ldw->creg_z = extractBits<4>(data, 28);
            ldw->dst = extractBits<5>(data, 23).to_ulong();
            ldw->baseR = extractBits<5>(data, 18).to_ulong();
            ldw->offsetR = extractBits<5>(data, 13).to_ulong();
            ldw->cst = ldw->offsetR;
            ldw->mode = extractBits<4>(data, 9).to_ulong();
            ldw->y = data[7];
            if (ldw->s == 0) {
                ldw->funcUnit = FuncUnit::D1;
            } else {
                ldw->funcUnit = FuncUnit::D2;
            }
            ldw->s = data[1];
            ldw->p = data[0];
            ldw->instFormat = InstFormat::Kind1;
        } else if (checkBits<5>(data, 2, 0b11011) && existStandardCondition(data)) {
//        LDW (.unit) *+B14/B15[ucst15], dst
            ldw = std::make_shared<LDW>(dspInstInfo->getAddress(), baseCycle);
            ldw->creg_z = extractBits<4>(data, 28);
            ldw->dst = extractBits<5>(data, 23).to_ulong();
            ldw->cst = extractBits<15>(data, 8).to_ulong();
            ldw->y = data[7];
            ldw->funcUnit = FuncUnit::D2;
            ldw->s = data[1];
            ldw->p = data[0];
            ldw->instFormat = InstFormat::Kind2;
        }
    } else {
        auto data = vectorToBitSet<16>(dspInstInfo->getBits());
        if (data[10] == 0 && checkBits<2>(data, 1, 0b10) && dspInstInfo->getDSZ()[2] == 0 && data[9] == 0 &&
            data[3] == 1) {
            ldw = std::make_shared<LDW>(dspInstInfo->getAddress(), baseCycle);
            ldw->isCompactInstruction = true;
            ldw->compactInstKind = "D0ff4";
            ldw->cst = extractBits<3>(data, 13).to_ulong();
            ldw->cst += static_cast<u32>(data[11]) << 3;
            ldw->s = data[12];     // t equal s in C8
            ldw->baseR = extractBits<2>(data, 7).to_ulong() + 4;
            ldw->dst = ldw->src1 = extractBits<3>(data, 4).to_ulong();
            ldw->mode = 0b0001;   // *+ptr[ucst4]
            ldw->y = data[0];       // s equal y in C8
            if (ldw->s == 0) {
                ldw->funcUnit = FuncUnit::D1;
            } else {
                ldw->funcUnit = FuncUnit::D2;
            }
            ldw->PROT = dspInstInfo->getPROT();
            ldw->BR = dspInstInfo->getBR();
            ldw->RS = dspInstInfo->getRS();
            ldw->SAT = dspInstInfo->getSAT();
            ldw->DSZ = dspInstInfo->getDSZ();
            ldw->p = dspInstInfo->getP();
            ldw->fixUpRegOnCompactInstruction();
            ldw->instFormat = InstFormat::Kind1;
        } else if (checkBits<2>(data, 10, 0b01) && checkBits<2>(data, 1, 0b10) && dspInstInfo->getDSZ()[2] == 0 &&
                   data[9] == 0 && data[3] == 1) {
            ldw = std::make_shared<LDW>(dspInstInfo->getAddress(), baseCycle);
            ldw->isCompactInstruction = true;
            ldw->compactInstKind = "Dind";
            ldw->offsetR = extractBits<3>(data, 13).to_ulong();
            ldw->s = data[12];     // t equal s in C8
            ldw->baseR = extractBits<2>(data, 7).to_ulong() + 4;
            ldw->dst = ldw->src1 = extractBits<3>(data, 4).to_ulong();
            ldw->mode = 0b0101;   // *+R[offsetR]
            ldw->y = data[0];       // s equal y in C10
            if (ldw->y == 0) {
                ldw->funcUnit = FuncUnit::D1;
            } else {
                ldw->funcUnit = FuncUnit::D2;
            }
            ldw->PROT = dspInstInfo->getPROT();
            ldw->BR = dspInstInfo->getBR();
            ldw->RS = dspInstInfo->getRS();
            ldw->DSZ = dspInstInfo->getDSZ();
            ldw->SAT = dspInstInfo->getSAT();
            ldw->p = dspInstInfo->getP();
            ldw->fixUpRegOnCompactInstruction();
            ldw->instFormat = InstFormat::Kind1;
        } else if (checkBits<2>(data, 14, 0b00) && checkBits<2>(data, 10, 0b11) && checkBits<2>(data, 1, 0b10) &&
                   dspInstInfo->getDSZ()[2] == 0 &&
                   data[9] == 0 && data[3] == 1) {
            ldw = std::make_shared<LDW>(dspInstInfo->getAddress(), baseCycle);
            ldw->isCompactInstruction = true;
            ldw->compactInstKind = "Dinc";
            ldw->cst = extractBits<1>(data, 13).to_ulong() + 1;
            ldw->s = data[12];     // t equal s
            ldw->baseR = extractBits<2>(data, 7).to_ulong() + 4;
            ldw->dst = ldw->src1 = extractBits<3>(data, 4).to_ulong();
            ldw->mode = 0b1011;   // *R++[ucst5]
            ldw->y = data[0];       // s equal y
            if (ldw->y == 0) {
                ldw->funcUnit = FuncUnit::D1;
            } else {
                ldw->funcUnit = FuncUnit::D2;
            }
            ldw->PROT = dspInstInfo->getPROT();
            ldw->BR = dspInstInfo->getBR();
            ldw->RS = dspInstInfo->getRS();
            ldw->SAT = dspInstInfo->getSAT();
            ldw->DSZ = dspInstInfo->getDSZ();
            ldw->p = dspInstInfo->getP();
            ldw->fixUpRegOnCompactInstruction();
            ldw->instFormat = InstFormat::Kind1;
        } else if (checkBits<2>(data, 14, 0b01) && checkBits<2>(data, 10, 0b11) && checkBits<2>(data, 1, 0b10) &&
                   dspInstInfo->getDSZ()[2] == 0 &&
                   data[9] == 0 && data[3] == 1) {
            ldw = std::make_shared<LDW>(dspInstInfo->getAddress(), baseCycle);
            ldw->isCompactInstruction = true;
            ldw->compactInstKind = "Ddec";
            ldw->cst = extractBits<1>(data, 13).to_ulong() + 1;
            ldw->s = data[12];     // t equal s in C
            ldw->baseR = extractBits<2>(data, 7).to_ulong() + 4;
            ldw->dst = ldw->src1 = extractBits<3>(data, 4).to_ulong();
            ldw->mode = 0b1000;   // *- -R[ucst5]
            ldw->y = data[0];       // s equal y in C
            if (ldw->y == 0) {
                ldw->funcUnit = FuncUnit::D1;
            } else {
                ldw->funcUnit = FuncUnit::D2;
            }
            ldw->PROT = dspInstInfo->getPROT();
            ldw->BR = dspInstInfo->getBR();
            ldw->RS = dspInstInfo->getRS();
            ldw->DSZ = dspInstInfo->getDSZ();
            ldw->SAT = dspInstInfo->getSAT();
            ldw->p = dspInstInfo->getP();
            ldw->fixUpRegOnCompactInstruction();
            ldw->instFormat = InstFormat::Kind1;
        } else if (data[15] == 1 && checkBits<2>(data, 10, 0b11) && checkBits<2>(data, 1, 0b10) && data[3] == 1 &&
                   data[0] == 1) {
            ldw = std::make_shared<LDW>(dspInstInfo->getAddress(), baseCycle);
            ldw->isCompactInstruction = true;
            ldw->compactInstKind = "Dstk";
            ldw->cst = extractBits<2>(data, 13).to_ulong() + (extractBits<3>(data, 7).to_ulong() << 2);
            ldw->s = data[12];     // t equal s in C
            ldw->baseR = 15;
            ldw->y = 1;
            ldw->dst = extractBits<3>(data, 4).to_ulong();
            ldw->mode = 0b0001;      // *+ptr[ucst15]
            ldw->funcUnit = FuncUnit::D2;
            ldw->PROT = dspInstInfo->getPROT();
            ldw->BR = dspInstInfo->getBR();
            ldw->RS = dspInstInfo->getRS();
            ldw->DSZ = dspInstInfo->getDSZ();
            ldw->SAT = dspInstInfo->getSAT();
            ldw->p = dspInstInfo->getP();
            ldw->fixUpRegOnCompactInstruction();
            ldw->instFormat = InstFormat::Kind2;
        } else if (checkBits<7>(data, 0, 0b1110111) && data[11] == 0 && data[15] == 0 && data[14] == 1) {
            // STW (.unit) src,*B15--[ucst2]
            ldw = std::make_shared<LDW>(dspInstInfo->getAddress(), baseCycle);
            ldw->isCompactInstruction = true;
            ldw->compactInstKind = "Dpp";
            // 基址寄存器选择B15
            ldw->baseR = 15;
            ldw->y = 1;
            // offset
            ldw->cst = data[13];
            ldw->cst += 1;
            // if t(s) == 1,dst为BReg,否则dst为AReg
            ldw->s = data[12];
            ldw->dst = extractBits<4>(data, 7).to_ulong();
            ldw->funcUnit = FuncUnit::D2;
            // *++B15[ucst]
            ldw->mode = 0b1001;
            ldw->PROT = dspInstInfo->getPROT();
            ldw->BR = dspInstInfo->getBR();
            ldw->RS = dspInstInfo->getRS();
            ldw->DSZ = dspInstInfo->getDSZ();
            ldw->SAT = dspInstInfo->getSAT();
            ldw->p = dspInstInfo->getP();
            ldw->fixUpRegOnCompactInstruction();
            ldw->instFormat = InstFormat::Kind1;
        }
    }
    return ldw;
}

void LDW::executeCustom(std::shared_ptr<TargetMachine> targetMachine, ExecutePhase executePhase) {
    auto dspMachine = std::dynamic_pointer_cast<DspMachine>(targetMachine);
    if (executePhase == ExecutePhase::E1) {
        if (instFormat == InstFormat::Kind1) {
            u32 *baseRReadPtr;
            u32 *baseRWritePtr;
            if (y == 0) {
                baseRReadPtr = dspMachine->getSaveRegs()->getReg32ByName(RegAName[baseR])->getDataPtr();
                baseRWritePtr = dspMachine->getRegisters()->getReg32ByName(RegAName[baseR])->getDataPtr();
            } else {
                baseRReadPtr = dspMachine->getSaveRegs()->getReg32ByName(RegBName[baseR])->getDataPtr();
                baseRWritePtr = dspMachine->getRegisters()->getReg32ByName(RegBName[baseR])->getDataPtr();
            }
            m_visitMemAddr = computeRealAddress(baseRReadPtr, baseRWritePtr, mode, 2, dspMachine);
        } else if (instFormat == InstFormat::Kind2) {
            u32 *baseRPtr;
            if (y == 0) {
                baseRPtr = dspMachine->getSaveRegs()->getReg32ByName("B14")->getDataPtr();
            } else {
                baseRPtr = dspMachine->getSaveRegs()->getReg32ByName("B15")->getDataPtr();
            }
            m_visitMemAddr = *baseRPtr + (cst << 2);
        }
    } else if (executePhase == ExecutePhase::E3) {
        m_data = dspMachine->getMMU()->getMemory()->readNBytes(m_visitMemAddr, 4);
    } else if (executePhase == ExecutePhase::E5) {
        *dstLoRegPtr = vectorToBitSet<32>(m_data).to_ulong();
    }
}

std::string LDW::toString() const {
    std::string res;
    if (!isCompactInstruction) {
        res += getStandardConditionString() + " ";
    }
    res += m_name + " " + FuncUnitName[static_cast<u32>(funcUnit)] + " ";
    switch (instFormat) {
        case InstFormat::Kind1:
            res += addressModeToString() + "," + getDstReg32Name();
            break;
        case InstFormat::Kind2:
            if (y == 0) {
                res = res + " B14[" + std::to_string(cst) + "]," + getDstReg32Name();
            } else {
                res = res + " B15[" + std::to_string(cst) + "]," + getDstReg32Name();
            }
            break;
        default:
            break;
    }
    return res;
}

std::shared_ptr<DspInstruction> LDW::clone() {
    return std::make_shared<LDW>(*this);
}

